<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Device</title>
<script src=http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js></script>
<script>
var Device = top.Tian.Class(top.Tian.Embeded, {
    dictionaries: {
        'en': {
            'filter': 'Filter',
            'add': 'Create',
            'delete': 'Delete',
            'save': 'Update',
            'confirm': 'All data of this device can NOT be accessed after this action, continue?',
            'sn_label': 'Serial number:',
            'model_label': 'Model:',
            'lonlat_label': 'Geolocation:',
            'title_label': 'Title:',
            'icon_label': 'Icon image (32x32):',
            'alarm_label': 'Alarm sound (<10k):',
            'prompt_sn': 'Device serial number:',
            'prompt_key': 'Device identification code:',
            'error_user': 'Please sign in to system first.',
            'error_illegal': 'Illegal account.',
            'error_inactive': 'Account is not activated yet.',
            'error_token': 'Your token has expired. Please sign in again.',
            'error_title': 'Invalid device title.',
            'error_icon': 'Invalid icon image URL.',
            'error_alarm': 'Invalid alarm sound URL.',
            'error_sn': 'Invalid device serial number.',
            'error_snkey': 'Invalid device serial number or wrong identification code.',
            'error_used': 'This device has been already created.',
            
            'processing': 'Processing...',
            'success': 'This action has already taken place.',
            'system_error': 'System error: ',
            'unknown_error': 'Unknown error.'
        },
        'zh-CN' : {
            'filter': '过滤',
            'add': '创建',
            'delete': '删除',
            'save': '更新',
            'confirm': '删除后将无法查询该设备的所有数据，是否继续？',
            'sn_label': '序列号：',
            'model_label': '型号：',
            'lonlat_label': '地理位置：',
            'title_label': '名称：',
            'icon_label': '图标 (32x32)：',
            'alarm_label': '警铃 (<10k)：',
            'prompt_sn': '设备序列号：',
            'prompt_key': '设备识别码：',
            'error_user': '请先登录系统。',
            'error_illegal': '非法用户。',
            'error_inactive': '用户还没有被激活。',
            'error_token': '您的令牌已过期，请重新登录。',
            'error_title': '无效的设备名称。',
            'error_icon': '无效的图标链接。',
            'error_alarm': '无效的警铃链接。',
            'error_sn': '无效的设备序列号。',
            'error_snkey': '无效的设备序列号或错误的设备识别码。',
            'error_used': '该设备已经被创建。',
            
            'processing': '处理中……',
            'success': '操作成功。',
            'system_error': '系统错误：',
            'unknown_error': '未知错误。'
        }
    },
    
    onRun: function () {
        top.Tian.Embeded.prototype.onRun.apply(this, arguments);
        
        // i18n
        $('#filter').val(this.i18n('filter'));
        $('#add').val(this.i18n('add'));
        $('#delete').val(this.i18n('delete'));
        $('#save').val(this.i18n('save'));
        $('#sn_label').text(this.i18n('sn_label'));
        $('#model_label').text(this.i18n('model_label'));
        $('#lonlat_label').text(this.i18n('lonlat_label'));
        $('#title_label').text(this.i18n('title_label'));
        $('#icon_label').text(this.i18n('icon_label'));
        $('#alarm_label').text(this.i18n('alarm_label'));
        
        // initialize device list
        var account = this.getOS().getAccount();
        if (account && account.devices) {
            this.initDevList(account.devices);
        }
        
        // attach events
        $('#mask').bind('keypress', function(event) {
            if (event.which == 13) {
                $('#filter').click();
                event.preventDefault();
            }
        });
        $('#device_title, #device_icon, #device_alarm').bind('keypress', function(event) {
            if (event.which == 13) {
                $('#save').click();
                event.preventDefault();
            }
        });
        $("#filter").bind('click', top.Tian.Function.bind(this.onFilter, this));
        $("#add").bind('click', top.Tian.Function.bind(this.onAdd, this));
        $("#delete").bind('click', top.Tian.Function.bind(this.onDelete, this));
        $("#save").bind('click', top.Tian.Function.bind(this.onSave, this));

        this.getOS().getEvents().register('socketwedata', this, this.onSocketWeData);
        this.getOS().getEvents().register('signin', this, this.onSignIn);
        this.getOS().getEvents().register('signout', this, this.onSignOut);
        this.getOS().getEvents().register('devicedragged', this, this.onDeviceDragged);
    },
    
    initDevList: function(devices) {
        if (top.Tian.Array.isArray(devices)) {
            $('#devicelist').empty();
            $('#device_sn,#device_model, #device_lonlat').text('');
            $('#device_title, #device_icon, #device_alarm').val('');
            
            for (var i=0, len=devices.length; i<len; i++) {
                var device = devices[i];
                $('#devicelist').append(
                    '<li id=' + device.sn +
                    ' data-sn=\"' + device.sn + '\" ' +
                    ' data-model=\"' + device.model + '\" ' +
                    ' data-lonlat=\"' + (device.opts.lonlat ? device.opts.lonlat[0]+','+device.opts.lonlat[1] : '0,0') + '\" ' +
                    ' data-title=\"' + device.opts.title + '\" ' +
                    ' data-icon=\"' + device.opts.icon + '\" ' +
                    ' data-alarm=\"' + device.opts.alarm + '\" >' +
                    device.opts.title + 
                    '</li>'
                );
            }
            
            $("#devicelist li").bind('click', top.Tian.Function.bind(this.onDeviceClick, this));
        }
    },
    
    destroy: function () {
        // remove event listeners
        $('input').unbind();
        $('#devicelist li').unbind();
        this.getOS().getEvents().unregister('socketwedata', this, this.onSocketWeData);
        this.getOS().getEvents().unregister('signin', this, this.onSignIn);
        this.getOS().getEvents().unregister('signout', this, this.onSignOut);
        this.getOS().getEvents().unregister('devicedragged', this, this.onDeviceDragged);
        
        top.Tian.Embeded.prototype.destroy.apply(this, arguments);
    },
    
    onFilter: function(event) {
        this.info('');
        $('#device_sn, #device_model, #device_lonlat').text('');
        $('#device_title, #device_icon, #device_alarm').val('');
        
        var mask = $('#mask').val().toLowerCase();
        if (!mask || mask.length<1) {
            // show all items
            $('#devicelist li').show();
            return;
        }
        
        // show masked items
        $('#devicelist li').each(function(idx) {
            var title = $(this).text().toLowerCase();
            if (title.indexOf(mask) != -1) {
                $(this).show();
            } else {
                $(this).hide();
            }
        });
    },
    
    onAdd: function(event) {
        this.info('');
        
        var account = this.getOS().getAccount();
        if (!account) {
            this.info(this.i18n('error_user'));
            return;
        }
        
        var sn = window.prompt(this.i18n('prompt_sn'), '00000000-000000-0000-000000');
        if (!sn) { // cancel
            return;
        }
        
        sn = sn.replace(/ /g, '').replace(/-/g, '').toLowerCase();
        var snReg = /^[a-f0-9]{24}$/;
        if (!snReg.test(sn)) {
            this.info(this.i18n('error_sn'));
            return;
        }
        
        var key = top.Tian.String.trim(window.prompt(this.i18n('prompt_key'), '00000000-0000-0000-0000-000000000000'));
        if (!key) { // cancel
            return;
        }
        
        key = key.replace(/ /g, '').replace(/-/g, '').toLowerCase();
        var keyReg = /^[a-f0-9]{32}$/;
        if (!keyReg.test(key)) {
            this.info(this.i18n('error_snkey'));
            return;
        }
        
        // create new device
        $('input').attr('disabled', 'disabled');
        this.getOS().getEvents().triggerEvent('wedata', {wedata: {
            type: 'devicecreate',
            user: account.user,
            token: account.token,
            sn: sn,
            key: key,
            lang: top.Tian.Lang.getCode()
        }});
    },
    
    onCreateFeedback: function(data) {
        // check status code
        switch (data.status) {
        case 0: // processing
            this.info(this.i18n('processing'));
            return;
        case 1: // db error
            this.info(this.i18n('system_error') + data.status);
            break;
        case 2: // invalid user
            this.info(this.i18n('error_illegal'));
            break;
        case 3: // account not activated
            this.info(this.i18n('error_inactive'));
            break;
        case 4: // token error
            this.info(this.i18n('error_token'));
            break;
        case 5: // db error
            this.info(this.i18n('system_error') + data.status);
            break;
        case 6: // invalid sn or key
            this.info(this.i18n('error_snkey'));
            break;
        case 7: // device used
            this.info(this.i18n('error_used'));
            break;
        case 8: // db error
            this.info(this.i18n('system_error') + data.status);
            break;
        case 9: // db error
            this.info(this.i18n('system_error') + data.status);
            break;
        case 10: // ok
            this.addDevice(data.device);
            this.info(this.i18n('success'));
            break;
        default:
            this.info(this.i18n('unknown_error'));
        }
        
        $('input').removeAttr('disabled');
    },
    
    addDevice: function(device) {
        // add to device list
        $('#devicelist').append(
            '<li id=' + device.sn +
            ' data-title=\"' + (device.opts.title || top.Tian.i18n('txDevice')) + '\" ' +
            ' data-lonlat=\"' + (device.opts.lonlat ? device.opts.lonlat[0]+','+device.opts.lonlat[1] : '0,0') + '\" ' +
            ' data-icon=\"' + (device.opts.icon || this.getOS().getImagePath() + 'default_device_icon.png') + '\" ' +
            ' data-alarm=\"' + (device.opts.alarm || this.getOS().getImagePath() + 'default_device_alarm.mp3') + '\" ' +
            ' data-model=\"' + device.model + '\" ' +
            ' data-sn=\"' + device.sn + '\" >' +
            (device.opts.title || top.Tian.i18n('txDevice')) + '</li>'
        );
        
        $("#devicelist li").bind('click', top.Tian.Function.bind(this.onDeviceClick, this));
        $('.device').scrollTop($('#devicelist').height());
        $('#devicelist li:last').click();
        
        // tell os
        this.getOS().getEvents().triggerEvent('devicecreate', {device: device});
    },
    
    onDelete: function(event) {
        this.info('');
        
        var account = this.getOS().getAccount();
        if (!account) {
            this.info(this.i18n('error_user'));
            return;
        }
        
        var sn = top.Tian.String.trim($('#device_sn').text());
        sn = sn.replace(/ /g, '').replace(/-/g, '').toLowerCase();
        if (!sn) {
            this.info(this.i18n('system_error'));
            return;
        }
        
        if (!confirm(this.i18n('confirm'))) {
            return;
        }
        
        $('input').attr('disabled', 'disabled');
        this.getOS().getEvents().triggerEvent('wedata', {wedata: {
            type: 'devicedelete',
            user: account.user,
            token: account.token,
            sn: sn,
            lang: top.Tian.Lang.getCode()
        }});
    },
    
    onDeleteFeedback: function(data) {
        // check status code
        switch (data.status) {
        case 0: // processing
            this.info(this.i18n('processing'));
            return;
        case 1: // db error
            this.info(this.i18n('system_error') + data.status);
            break;
        case 2: // invalid user
            this.info(this.i18n('error_illegal'));
            break;
        case 3: // account not activated
            this.info(this.i18n('error_inactive'));
            break;
        case 4: // token error
            this.info(this.i18n('error_token'));
            break;
        case 5: // db error
            this.info(this.i18n('system_error') + data.status);
            break;
        case 6: // db error
            this.info(this.i18n('system_error') + data.status);
            this.deleteDevice(data.sn);
            break;
        case 7: // invalid sn
            this.info(this.i18n('error_sn'));
            this.deleteDevice(data.sn);
            break;
        case 8: // db error
            this.info(this.i18n('system_error') + data.status);
            this.deleteDevice(data.sn);
            break;
        case 9: // ok
            this.info(this.i18n('success'));
            this.deleteDevice(data.sn);
            break;
        default:
            this.info(this.i18n('unknown_error'));
        }
        
        $('input').removeAttr('disabled');
    },
    
    deleteDevice: function(sn) {
        // remove from device list
        $('#devicelist #'+sn).remove();
        $('#device_sn, #device_model, #device_lonlat').text('');
        $('#device_title, #device_icon, #device_alarm').val('');
        
        // tell os
        this.getOS().getEvents().triggerEvent('devicedelete', {device: {sn: sn}});
    },
    
    onSave: function(event) {
        this.info('');
        
        var account = this.getOS().getAccount();
        if (!account) {
            this.info(this.i18n('error_user'));
            return;
        }
        
        // validate input data
        var title = top.Tian.String.trim($('#device_title').val()),
            icon = top.Tian.String.trim($('#device_icon').val()),
            alarm = top.Tian.String.trim($('#device_alarm').val()),
            sn = top.Tian.String.trim($('#device_sn').text()),
            lonlat = top.Tian.String.trim($('#device_lonlat').text());
        
        sn = sn.replace(/ /g, '').replace(/-/g, '').toLowerCase();
        if (!sn) {
            this.info(this.i18n('error_sn'));
            return;
        }
        if (!lonlat) {
            this.info(this.i18n('system_error'));
            return;
        }
        if (!title) {
            this.info(this.i18n('error_title'));
            return;
        }
        if (!top.Tian.String.isURL(icon)) {
            this.info(this.i18n('error_icon'));
            return;
        }
        if (!top.Tian.String.isURL(alarm)) {
            this.info(this.i18n('error_alarm'));
            return;
        }
        
        lonlat = lonlat.split(',');
        lonlat[0] = (parseFloat(lonlat[0]) || 0.000000);
        lonlat[1] = (parseFloat(lonlat[1]) || 0.000000);
        
        $('input').attr('disabled', 'disabled');
        this.getOS().getEvents().triggerEvent('wedata', {wedata: {
            type: 'deviceupdate',
            user: account.user,
            token: account.token,
            sn: sn,
            lonlat: lonlat,
            title: title,
            icon: icon,
            alarm: alarm,
            lang: top.Tian.Lang.getCode()
        }});
    },
    
    onUpdateFeedback: function(data) {
        // check status code
        switch (data.status) {
        case 0: // processing
            this.info(this.i18n('processing'));
            return;
        case 1: // db error
            this.info(this.i18n('system_error') + data.status);
            break;
        case 2: // invalid user
            this.info(this.i18n('error_illegal'));
            break;
        case 3: // account not activated
            this.info(this.i18n('error_inactive'));
            break;
        case 4: // token error
            this.info(this.i18n('error_token'));
            break;
        case 5: // db error
            this.info(this.i18n('system_error') + data.status);
            break;
        case 6: // ok
            this.info(this.i18n('success'));
            this.updateDevice(data);
            break;
        default:
            this.info(this.i18n('unknown_error'));
        }
        
        $('input').removeAttr('disabled');
    },
    
    updateDevice: function(device) {
        // update local copy
        var dom = $('#devicelist #' + device.sn);
        dom.data('title', device.title);
        dom.data('icon', device.icon);
        dom.data('alarm', device.alarm);
        dom.data('lonlat', device.lonlat ? device.lonlat[0]+','+device.lonlat[1] : '0,0');
        dom.text(device.title);
        
        // tell os
        this.getOS().getEvents().triggerEvent('deviceupdate', {
            device: {
                sn: device.sn,
                lonlat: device.lonlat,
                title: device.title,
                icon: device.icon,
                alarm: device.alarm
            }
        });
    },
    
    onDeviceClick: function(event) {
        if (!event || !event.currentTarget) {
            return;
        }
        
        this.info('');
        
        $('#devicelist li').removeClass('clicked');
        $(event.currentTarget).addClass('clicked');
        
        // fill editbox with dataset of event target
        var sn = $(event.currentTarget).data('sn');
        sn = sn.slice(0,8) + '-' + sn.slice(8,14) + '-' + sn.slice(14,18) + '-' + sn.slice(18);
        $('#device_sn').text(sn);
        $('#device_model').text($(event.currentTarget).data('model'));
        $('#device_lonlat').text($(event.currentTarget).data('lonlat'));
        $('#device_title').val($(event.currentTarget).data('title'));
        $('#device_icon').val($(event.currentTarget).data('icon'));
        $('#device_alarm').val($(event.currentTarget).data('alarm'));
    },
    
    info: function(str) {
        $('#info').text(str);
    },
    
    onSignIn: function(event) {
        if (event.account && event.account.devices) {
            this.initDevList(event.account.devices);
        }
    },
    
    onSignOut: function(event) {
        $('#devicelist').empty();
        $('#device_sn, #device_model, #device_lonlat').text('');
        $('#device_title, #device_icon, #device_alarm').val('');
    },
    
    onDeviceDragged: function(event) {
        if (!event || !event.device) {
            return;
        }
        
        var device = event.device;
        var sn = top.Tian.String.trim($('#device_sn').text());
        sn = sn.replace(/ /g, '').replace(/-/g, '').toLowerCase();
        
        if (sn === device.sn && top.Tian.Array.isArray(device.lonlat) && device.lonlat.length === 2) {
            $('#device_lonlat').text(device.lonlat[0] + ',' + device.lonlat[1]);
        }
    },
    
    onSocketWeData: function(event) {
        if (!event || !event.wedata) {
            return;
        }
        
        var data = event.wedata;
        switch (data.type) {
        case 'devicecreate':
            this.onCreateFeedback(data);
            break;
        case 'deviceupdate':
            this.onUpdateFeedback(data);
            break;
        case 'devicedelete':
            this.onDeleteFeedback(data);
            break;
        default:
        }
    },
    
    CLASS_NAME: 'Device'
});

function tianMain(options) {
    return new Device(options);
}
</script>

<style type='text/css'>
body {
    font-size: 12px;
    margin: 0px;
    padding: 0px;
}

.listbox {
    position: absolute;
    top: 0px;
    left: 0px;
    bottom: 0px;
    width: 200px;
    overflow: hidden;
    border-right: solid 1px #ddd;
}

.editbox {
    position: absolute;
    top: 0px;
    right: 0px;
    bottom: 0px;
    left: 205px;
    overflow: auto;
}
p {
    padding: 0px;
    margin: 5px 0 0 0;
}
.inputs {
    padding: 5px;
}

input[type=text] {
    width: 300px;
    border: solid 1px gray;
}
input[type=button] {
    width: 80px;
}
#mask {
    width: 100px;
}
#filter {
    width: 60px;
}

.device {
    position: absolute;
    top: 36px;
    left: 0px;
    right: 0px;
    bottom: 0px;
    overflow: auto;
    white-space: nowrap;
}
ol {
    margin: 0px;
}
ol li:hover {
    cursor: pointer;
    background-color: #ddd;
}
.clicked {
    background-color: #ddd;
}
#device_sn, #device_model, #device_lonlat {
    font-family: "Courier New";
}
</style>

</head>
<body>
<div class=listbox>
    <div class=inputs>
        <input id=mask type=text />
        <input id=filter type=button value=Filter />
    </div>
    <div class=device>
        <ol id=devicelist></ol>
    </div>
</div>
<div class=editbox>
    <div class=inputs>
        <p>
            <label id=sn_label>Serial number:</label>
            <label id=device_sn></label>
        </p>
        <p>
            <label id=model_label>Model:</label>
            <label id=device_model></label>
        </p>
        <p>
            <label id=lonlat_label>Geolocation:</label>
            <label id=device_lonlat></label>
        </p>
        <p>
            <label id=title_label>Title:</label><br />
            <input id=device_title type=text />
        </p>
        <p>
            <label id=icon_label>Icon image:</label>
            <input id=device_icon type=text />
        </p>
        <p>
            <label id=alarm_label>Alarm sound:</label>
            <input id=device_alarm type=text />
        </p>
        <p id=info></p>
    </div>
    <div class=inputs>
        <input id=add type=button value=Add />
        <input id=delete type=button value=Delete />
        <input id=save type=button value=Save />
    </div>
</div>
</body>
</html>
